import time
import struct
import random
import socks
import uuid
from paho.mqtt import client as mqtt_client

# 测试，使用的是 cabinet 模板的用户名密码
broker = 'afubzfb.iot.gz.baidubce.com'
port = 1883
client_id = f'python-mqtt-{random.randint(0, 1000)}'
username = "thingidp@afubzfb|test_cabinet|0|MD5"
password = "0f59bf40622ba73521152222134285b2"

# 这些二进制转换应该均用的是小端模式
# float 的转换直接搬运自 BMS
def bytes_to_float(h1, h2, h3, h4):
    ba = bytearray()
    ba.append(h1)
    ba.append(h2)
    ba.append(h3)
    ba.append(h4)
    return struct.unpack("!f", ba)[0]


def float_to_bytes(f):
    bs = struct.pack("f", f)
    return bs[3], bs[2], bs[1], bs[0]

# 参照了 BMS 的 float 转换，使用 struct.unpack 转换 short
def bytes_to_short(h1, h2):
    ba = bytearray()
    ba.append(h1)
    ba.append(h2)
    return struct.unpack('!h', ba)[0]


def short_to_bytes(h):
    bs = struct.pack('h', h)
    return bs[1], bs[0]

# 参照了 BMS 的 float 转换，使用 struct.unpack 转换 unsigned short
def bytes_to_u_short(h1, h2):
    ba = bytearray()
    ba.append(h1)
    ba.append(h2)
    return struct.unpack('!H', ba)[0]


def u_short_to_bytes(h):
    bs = struct.pack('H', h)
    return bs[1], bs[0]

# bytes 转 string，按照 utf-8 编码解码转换，但不会解码0x00 
def bytes_to_str(bytesarr):
    if bytesarr == None:
        return
    new_arr = bytearray()
    for b in bytesarr:
        if b != 0x00:
            new_arr.append(b)
    return new_arr.decode('utf-8')


def str_to_bytes(s, target_bytes_len):
    s = bytes(s.encode('utf-8'))
    s = bytearray(s)
    if len(s) > target_bytes_len:
        print('[ERR] string is too long for target_bytes_len')
        return
    elif len(s) == target_bytes_len:
        return bytes(s)
    else:
        for i in range(target_bytes_len - len(s)):
            s.append(0x00)
        return bytes(s)


def print_hex(bytes):
    l = [hex(int(i)) for i in bytes]
    print(" ".join(l))


def connect_mqtt():
    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            print("Connected to MQTT Broker!")
        else:
            print("Failed to connect, return code %d\n", rc)

    # Set Connecting Client ID
    client = mqtt_client.Client(client_id)
    client.username_pw_set(username, password)
    # 按需设置代理，因为测试时发现如果不指定走代理mqtt连接就会很慢，如果电脑上没有代理，那就注释掉下面那一行
    client.proxy_set(proxy_type=socks.SOCKS5, proxy_addr='127.0.0.1', proxy_port=7890) 
    client.will_set(topic="cabinet/offline", payload=r'emulator, offline', qos=0, retain=True)
    client.on_connect = on_connect
    client.connect(broker, port)
    return client


def publish_test(client, topic):
    msg_count = 0
    while True:
        time.sleep(1)
        msg = f"messages: {msg_count}"
        result = client.publish(topic, msg)
        # result: [0, 1]
        status = result[0]
        if status == 0:
            print(f"Send `{msg}` to topic `{topic}`")
        else:
            print(f"Failed to send message to topic {topic}")
        msg_count += 1


def subscribe(client: mqtt_client, topic, on_message):
    client.subscribe(topic)
    client.on_message = on_message


def on_message_received(client, userdata, msg):
    print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")


def gen_payload():
    battery_num = 4  
    length = 26 + battery_num * 38
    payload = bytearray(length)

    # 电池柜编号 str
    s = 'cab-'+str(uuid.uuid1())[0:8]
    print(s)
    s = str_to_bytes(s, target_bytes_len=12)
    for i in range(12):
        payload[i] = s[i]
    # 水位报警 us
    s = u_short_to_bytes(0)
    payload[12] = s[0]
    payload[13] = s[1]
    # 火灾报警 us
    s = u_short_to_bytes(65535)
    payload[14] = s[0]
    payload[15] = s[1]
    # GSM信号强度 s
    s = short_to_bytes(-32768)
    payload[16] = s[0]
    payload[17] = s[1]
    # 电池柜所在的经度 f
    s = float_to_bytes(108.12345)
    payload[18] = s[0]
    payload[19] = s[1]
    payload[20] = s[2]
    payload[21] = s[3]
    # 电池柜所在的纬度 f
    s = float_to_bytes(34.12345)
    payload[22] = s[0]
    payload[23] = s[1]
    payload[24] = s[2]
    payload[25] = s[3]

    # 电池部分
    for i in range(battery_num):
        print(str(i+1)+'#')
        offset = 26 + 38 * i
        # '电池的充电状态': us 1,
        s = u_short_to_bytes(1)
        payload[offset + 0] = s[0]
        payload[offset + 1] = s[1]
        # '电池的充电电压': us 132, 132/10=13.2V
        v = random.randint(0,256)
        s = u_short_to_bytes(v)
        print(v)
        payload[offset + 2] = s[0]
        payload[offset + 3] = s[1]
        # '电池的充电电流': s -76, 76/10=7.6A
        s = short_to_bytes(random.randint(-128,128))
        payload[offset + 4] = s[0]
        payload[offset + 5] = s[1]
        # '电池的充电机状态': us 0,
        s = u_short_to_bytes(0)
        payload[offset + 6] = s[0]
        payload[offset + 7] = s[1]
        # '电池编号': str 'b0000001',
        s = 'bat-' + str(uuid.uuid1())[3:6] + str(i)
        print(s)
        s = str_to_bytes(s, target_bytes_len=8)
        for i in range(8):
            payload[offset + 8 + i] = s[i]
        # '柜门内电池类型': us 2,
        s = u_short_to_bytes(random.randint(0,2))
        payload[offset + 16] = s[0]
        payload[offset + 17] = s[1]
        # '柜门状态': us 1,
        s = u_short_to_bytes(random.randint(0,1))
        payload[offset + 18] = s[0]
        payload[offset + 19] = s[1]
        # '柜仓温度': s 345, 345/10=34.5
        s = short_to_bytes(random.randint(200,450))
        payload[offset + 20] = s[0]
        payload[offset + 21] = s[1]
        # '电池剩余电量所占的百分比': us 98,
        s = u_short_to_bytes(random.randint(0,100))
        payload[offset + 22] = s[0]
        payload[offset + 23] = s[1]
        # '电池的剩余容量': us 97,
        s = u_short_to_bytes(random.randint(0,100))
        payload[offset + 24] = s[0]
        payload[offset + 25] = s[1]
        # '电池的电压': us 101, 101/10=10.1
        s = u_short_to_bytes(random.randint(10,200))
        payload[offset + 26] = s[0]
        payload[offset + 27] = s[1]
        # '电池的电芯温度': s 243, 243/10=24.3
        s = short_to_bytes(random.randint(200,450))
        payload[offset + 28] = s[0]
        payload[offset + 29] = s[1]
        # '电池所在的经度': 108.12346,
        s = float_to_bytes(123.67891)
        payload[offset + 30] = s[0]
        payload[offset + 31] = s[1]
        payload[offset + 32] = s[2]
        payload[offset + 33] = s[3]
        # '电池所在的纬度': 34.12347
        s = float_to_bytes(41.12345)
        payload[offset + 34] = s[0]
        payload[offset + 35] = s[1]
        payload[offset + 36] = s[2]
        payload[offset + 37] = s[3]
    return payload


if __name__ == '__main__':
    client = connect_mqtt()
    client.loop_start()
    time.sleep(1)

    for i in range(5):
        p = gen_payload()
        client.publish("cabinet/state", p)
        # print(gen_payload())
        print_hex(p)
        print('send')
    time.sleep(1)
    client.loop_stop()
    client.disconnect()
